﻿using System.Windows.Automation;


namespace RigLib
{
    internal sealed class EventRegistrationMessage
    {
        internal AutomationEvent EventType;
        internal AutomationProperty Property;
        internal BaseProdControl Source;

        /// <summary>
        /// Initializes a new instance of the <see cref="EventRegistrationMessage"/> class.
        /// </summary>
        /// <param name="source">The source control.</param>
        /// <param name="property">The property to monitor.</param>
        internal EventRegistrationMessage(BaseProdControl source, AutomationProperty property)
        {
            Source = source;
            Property = property;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="EventRegistrationMessage"/> class.
        /// </summary>
        /// <param name="source">The source control.</param>
        /// <param name="eventType">Type of the event.</param>
        internal EventRegistrationMessage(BaseProdControl source, AutomationEvent eventType)
        {
            Source = source;
            EventType = eventType;
        }
    }

    internal sealed class EventListener
    {
        private readonly EventRegistrationMessage _targetEvent;
        private AutomationEventHandler _eventHandler;
        private AutomationPropertyChangedEventHandler _propertyChangeHandler;

        /// <summary>
        /// Initializes a new instance of the <see cref="EventListener"/> class.
        /// </summary>
        /// <param name="targetEvent">The target event message.</param>
        internal EventListener(EventRegistrationMessage targetEvent)
        {
            if (targetEvent == null) throw new ProdOperationException("targetEvent cannot be null");
            _targetEvent = targetEvent;
            SetEventHook();
        }

        /// <summary>
        /// Sets the event listener hook depending on event type.
        /// </summary>
        private void SetEventHook()
        {
            /* se a property change hook */
            if (_targetEvent.Property != null)
            {
                _propertyChangeHandler = OnPropertyChange;
                Automation.AddAutomationPropertyChangedEventHandler(_targetEvent.Source.UIAElement, TreeScope.Element,
                                                                    _propertyChangeHandler, _targetEvent.Property);
                return;
            }

            /* or a straight event handler */
            _eventHandler = OnAutomationEvent;
            Automation.AddAutomationEventHandler(_targetEvent.EventType, _targetEvent.Source.UIAElement,
                                                 TreeScope.Element, _eventHandler);
        }

        /// <summary>
        /// Automation Event handler
        /// </summary>
        /// <param name="src">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Automation.AutomationEventArgs"/> instance containing the event data.</param>
        private void OnAutomationEvent(object src, AutomationEventArgs e)
        {
            if (src == null)
            {
                return;
            }

            if (e.EventId != _targetEvent.EventType)
            {
                AutomationEventVerifier.EventFired(_targetEvent);
                return;
            }

            RemoveHandler();
        }

        /// <summary>
        /// Handler for property change events
        /// </summary>
        /// <param name="src">The source whose properties changed.</param>
        /// <param name="e">Event arguments.</param>
        private void OnPropertyChange(object src, AutomationPropertyChangedEventArgs e)
        {
            if (src == null)
            {
                return;
            }

            AutomationEventVerifier.EventFired(_targetEvent);

            RemovePropertyChangeHandler();
        }

        /// <summary>
        /// Removes the handler.
        /// </summary>
        private void RemoveHandler()
        {
            Automation.RemoveAutomationEventHandler(_targetEvent.EventType, _targetEvent.Source.UIAElement,
                                                    _eventHandler);
            _eventHandler = null;
        }

        /// <summary>
        /// Removes the property change handler.
        /// </summary>
        private void RemovePropertyChangeHandler()
        {
            Automation.RemoveAutomationPropertyChangedEventHandler(_targetEvent.Source.UIAElement,
                                                                   _propertyChangeHandler);
            _propertyChangeHandler = null;
        }


    }

    internal static class AutomationEventVerifier
    {
        internal static void Register(EventRegistrationMessage targetEvent)
        {
            new EventListener(targetEvent);
        }

        internal static void EventFired(EventRegistrationMessage targetEvent)
        {
            targetEvent.Source.ReceiveEventNotification(true);
        }

        internal static void EventFail(EventRegistrationMessage targetEvent)
        {
            targetEvent.Source.ReceiveEventNotification(false);
        }
    }

    internal static class StaticEvents
    {
        private static AutomationEventHandler _eventHandler;
        private static AutomationPropertyChangedEventHandler _propertyChangeHandler;
        private static AutomationEvent _patternEventType;
        private static AutomationElement _control;

        /// <summary>
        /// Registers a method that handles UI Automation events
        /// </summary>
        /// <param name="eventType">The specific event type to monitor</param>
        /// <param name="control">The control to monitor for events.</param>
        public static void RegisterEvent(AutomationEvent eventType, AutomationElement control)
        {
            _control = control;

            if (eventType.Id == InvokePattern.InvokedEvent.Id)
            {
                _eventHandler = OnAutomationEvent;
                Automation.AddAutomationEventHandler(eventType, control, TreeScope.Element, _eventHandler);
                _patternEventType = eventType;
                return;
            }

            SubscribeToChildNotification(eventType, control);
        }

        /// <summary>
        /// Registers a method that will handle property-changed events
        /// </summary>
        /// <param name="property">The automation property to monitor for a state change</param>
        /// <param name="control">The control to monitor for events.</param>
        public static void RegisterEvent(AutomationProperty property, AutomationElement control)
        {
            _control = control;
            _propertyChangeHandler = OnPropertyChange;
            Automation.AddAutomationPropertyChangedEventHandler(control, TreeScope.Element, _propertyChangeHandler,
                                                                property);
        }

        /// <summary>
        /// Registers a method that handles UI Automation events inside containers
        /// </summary>
        /// <param name="eventType">The specific event type to monitor</param>
        /// <param name="control">The control to monitor for events.</param>
        public static void SubscribeToChildNotification(AutomationEvent eventType, AutomationElement control)
        {
            _control = control;
            _patternEventType = eventType;
            _eventHandler = OnAutomationEvent;
            Automation.AddAutomationEventHandler(_patternEventType, control, TreeScope.Descendants, _eventHandler);
        }

        /// <summary>
        /// Called when a subscribed automation event fires.
        /// </summary>
        /// <param name="src">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Automation.AutomationEventArgs"/> instance containing the event data.</param>
        private static void OnAutomationEvent(object src, AutomationEventArgs e)
        {
            if (src == null)
            {
                return;
            }

            if (e.EventId != _patternEventType)
            {
                return;
            }

            if (_eventHandler == null)
            {
                return;
            }

            RemoveHandler();
        }

        /// <summary>
        /// Handler for property change events
        /// </summary>
        /// <param name="src">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Automation.AutomationPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnPropertyChange(object src, AutomationPropertyChangedEventArgs e)
        {
            if (src == null)
            {
                return;
            }

            if (_propertyChangeHandler == null)
            {
                return;
            }

            RemovePropertyChangeHandler();
        }

        /// <summary>
        /// Removes the calling AutomationEventHandle.
        /// </summary>
        private static void RemoveHandler()
        {
            Automation.RemoveAutomationEventHandler(_patternEventType, _control, _eventHandler);
            _eventHandler = null;
        }

        /// <summary>
        /// Removes the calling property change handler.
        /// </summary>
        private static void RemovePropertyChangeHandler()
        {
            Automation.RemoveAutomationPropertyChangedEventHandler(_control, _propertyChangeHandler);
            _propertyChangeHandler = null;
        }
    }

}